package server;

import java.util.List;

import common.CurrencyEnum;
import common.ServiceErrorException;
import common.ServiceResponseEnum;
import common.interfaces.AccountService;
import server.messageContent.BalanceEnquiryMsgContent;
import server.messageContent.CloseAccountMsgContent;
import server.messageContent.CreateAccountMsgContent;
import server.messageContent.DepositMsgContent;
import server.messageContent.TransferFundMsgContent;
import server.messageContent.WithdrawMsgContent;

public class AccountServiceImpl implements AccountService {
	private static AccountServiceImpl current;
	List<BankAccount> data;
	private static MonitorService monitorService = new MonitorService();;

	public static AccountServiceImpl getCurrent() {
		if (current == null) {
			List<BankAccount> data = BankAccount.PreLoadBankAccounts();
			current = new AccountServiceImpl(data);
		}
		return current;
	}

	public AccountServiceImpl(List<BankAccount> data) {
		this.data = data;

	}

	// Create account
	@Override
	public long Create(CreateAccountMsgContent account)
			throws ServiceErrorException {
		// TODO: check if the account exist or not before create
		BankAccount newAccount = new BankAccount();
		System.out.println("Creating a new account with "
				+ account.getHolderName() + " "
				+ new String(account.getPassword()) + " "
				+ account.getCurrency() + " " + account.getBalance());
		newAccount.setHolderName(account.getHolderName());
		newAccount.setPassword(account.getPassword());
		newAccount.setCurrency(account.getCurrency());
		newAccount.setBalance(account.getBalance());
		data.add(newAccount);

		long createdAccNumber = newAccount.getAccNumber();

		// Check Callback list, if there is any client registered, then send
		// notification
		if (!AccountServiceImpl.monitorService.callbackList.isEmpty()) {
			AccountServiceImpl.monitorService.CreateAccount(createdAccNumber);
		}
		return createdAccNumber;
	}

	// Close account
	@Override
	public int Close(String holderName, CloseAccountMsgContent account,
			String password) throws ServiceErrorException {
		long accNum = account.getAccNumber();
		int i;
		for (i = 0; i < data.size(); i++)
			if (data.get(i).getHolderName().equals(holderName)
					&& accNum == data.get(i).getAccNumber()) {
				if (password.equals(data.get(i).getPassword())) {
					data.remove(i);

					// Check Callback list, if there is any client registered,
					// then send notification
					if (!AccountServiceImpl.monitorService.callbackList
							.isEmpty()) {
						AccountServiceImpl.monitorService.CloseAccount(
								ServiceResponseEnum.CLOSE_SUCCESSFUL, accNum);
					}
					return 1;
				} else
					return -2;
			}

		return -1;
	}

	@Override
	public int Withdraw(WithdrawMsgContent account, float amountWithdraw,
			String password) throws ServiceErrorException {
		String holderName = account.getHolderName();
		float rate = 0;
		long accNum = account.getAccNumber();
		int i;
		CurrencyEnum currency = account.getCurrency();
		for (i = 0; i < data.size(); i++)
			if (data.get(i).getHolderName().equals(holderName)
					&& accNum == data.get(i).getAccNumber()) {
				if (password.equals(data.get(i).getPassword())) {
					if (data.get(i).getCurrency() == account.getCurrency())
						rate = 1;
					else if (data.get(i).getCurrency() == CurrencyEnum.SGD
							&& account.getCurrency() == CurrencyEnum.USD)
						rate = (float) 1.2;
					else if (data.get(i).getCurrency() == CurrencyEnum.USD
							&& account.getCurrency() == CurrencyEnum.SGD)
						rate = (float) (1 / 1.2);
					if (data.get(i).getBalance() - rate * amountWithdraw >= 0) {
						data.get(i).setBalance(
								data.get(i).getBalance() - rate
										* amountWithdraw);

						// Check Callback list, if there is any client
						// registered, then send notification
						if (!AccountServiceImpl.monitorService.callbackList
								.isEmpty()) {
							AccountServiceImpl.monitorService.Withdraw(
									ServiceResponseEnum.SUCCESSFUL_WITHDRAW,
									accNum, amountWithdraw, currency);
						}
						return 1;
					} else {
						return 0;
					}
				} else
					return -2;
			}

		return -1;
	}

	@Override
	public float BalanceEnquiry(long accId) throws ServiceErrorException {
		float balance = -1;
		int i;
		for (i = 0; i < data.size(); i++)
			if (accId == data.get(i).getAccNumber()) {
				balance = data.get(i).getBalance();
				// String response = data.get(i).getCurrency().getMsg() +
				// balance;
				return balance;
			}
		return balance;
	}

	// Enquire Balance
	@Override
	public float EnquireBal(BalanceEnquiryMsgContent account, String password)
			throws ServiceErrorException {
		float balance = -1;
		int i;
		String holderName = account.getHolderName();
		long accNum = account.getAccountNumber();
		for (i = 0; i < data.size(); i++)
			if (data.get(i).getHolderName().equals(holderName)
					&& accNum == data.get(i).getAccNumber()) {
				if (password.equals(data.get(i).getPassword())) {
					balance = data.get(i).getBalance();
					return balance;
				} else {
					return -2;
				}
			}
		return balance;
	}

	@Override
	public ServiceResponseEnum Transfer(TransferFundMsgContent account)
			throws ServiceErrorException {
		String password = account.getSenderPassword();
		float rateSender = 0, rateReceiver = 0;
		String senderName = account.getSenderName();
		String recipientName = account.getRecipientName();
		long accNumSender = account.getSenderAccId();
		long accNumRecipient = account.getRecipientAccId();
		CurrencyEnum current = account.getCurrency();
		float amountTransfer = account.getAmount();
		int i;
		int j;
		for (i = 0; i < data.size(); i++)
			if (data.get(i).getHolderName().equals(senderName)
					&& accNumSender == data.get(i).getAccNumber()) {
				if (password.equals(data.get(i).getPassword())) {

					for (j = 0; j < data.size(); j++)
						if (data.get(j).getHolderName().equals(recipientName)
								&& accNumRecipient == data.get(j)
										.getAccNumber()) {
							// Calculate exchange rate for sender
							if (data.get(i).getCurrency() == account
									.getCurrency())
								rateSender = 1;
							else if (data.get(i).getCurrency() == CurrencyEnum.SGD
									&& account.getCurrency() == CurrencyEnum.USD)
								rateSender = (float) 1.2;
							else if (data.get(i).getCurrency() == CurrencyEnum.USD
									&& account.getCurrency() == CurrencyEnum.SGD)
								rateSender = (float) (1 / 1.2);

							// Calculate exchange rate for receiver
							if (data.get(j).getCurrency() == account
									.getCurrency())
								rateReceiver = 1;
							else if (data.get(j).getCurrency() == CurrencyEnum.SGD
									&& account.getCurrency() == CurrencyEnum.USD)
								rateReceiver = (float) 1.2;
							else if (data.get(j).getCurrency() == CurrencyEnum.USD
									&& account.getCurrency() == CurrencyEnum.SGD)
								rateReceiver = (float) (1 / 1.2);

							if (data.get(i).getBalance() - rateSender
									* amountTransfer >= 0) {
								data.get(i).setBalance(
										data.get(i).getBalance() - rateSender
												* amountTransfer);
								data.get(j).setBalance(
										data.get(j).getBalance() + rateReceiver
												* amountTransfer);
								if (!AccountServiceImpl.monitorService.callbackList
										.isEmpty()) {
									AccountServiceImpl.monitorService
											.Transfer(
													ServiceResponseEnum.SUCCESS_TRANSFER_FUND,
													accNumSender,
													accNumRecipient,
													amountTransfer, current);
								}
								return ServiceResponseEnum.SUCCESS_TRANSFER_FUND; // successful
																					// deducted
																					// sender
																					// balance
							} else
								return ServiceResponseEnum.INSUFFICIENT_SENDER_BALANCE; // insuffcient
																						// sender
																						// balance
						}
					return ServiceResponseEnum.NON_EXISTING_RECIPIENT_ACCOUNT; // no
																				// receiver
																				// found
				} else
					return ServiceResponseEnum.SENDER_ACCOUNT_WRONG_PASSWORD;
			}

		// Cannot find sender account
		return ServiceResponseEnum.NON_EXISTING_SENDER_ACCOUNT;
	}

	@Override
	public CurrencyEnum getCurrency(long accId) throws ServiceErrorException {
		int i;
		for (i = 0; i < data.size(); i++)
			if (accId == data.get(i).getAccNumber())
				return data.get(i).getCurrency();
		return null;
	}

	@Override
	public int Deposit(DepositMsgContent account, float amountDeposit,
			String password) throws ServiceErrorException {
		// TODO Auto-generated method stub
		String holderName = account.getHolderName();
		float rate = 0;
		long accNum = account.getAccNumber();
		int i;
		CurrencyEnum currency = account.getCurrency();
		for (i = 0; i < data.size(); i++)
			if (data.get(i).getHolderName().equals(holderName)
					&& accNum == data.get(i).getAccNumber()) {
				if (password.equals(data.get(i).getPassword())) {
					if (data.get(i).getCurrency() == account.getCurrency())
						rate = 1;
					else if (data.get(i).getCurrency() == CurrencyEnum.SGD
							&& account.getCurrency() == CurrencyEnum.USD)
						rate = (float) 1.2;
					else if (data.get(i).getCurrency() == CurrencyEnum.USD
							&& account.getCurrency() == CurrencyEnum.SGD)
						rate = (float) (1 / 1.2);
					data.get(i).setBalance(
							data.get(i).getBalance() + rate * amountDeposit);

					// Check Callback list, if there is any client registered,
					// then send notification
					if (!AccountServiceImpl.monitorService.callbackList
							.isEmpty()) {
						AccountServiceImpl.monitorService.Deposit(
								ServiceResponseEnum.SUCCESSFUL_DEPOSIT, accNum,
								amountDeposit, currency);
					}
					return 1;
				} else
					return -2;
			}
		return -1;
	}

	public static MonitorService getMonitorService() {
		return monitorService;
	}

	public static void setMonitorService(MonitorService monitorService) {
		AccountServiceImpl.monitorService = monitorService;
	}

}
